package com.star.cat.persitent.service.impl;

import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.star.cat.common.utils.DateUtil;
import com.star.cat.persitent.config.PerProperties;
import com.star.cat.persitent.model.PerModel;
import com.star.cat.persitent.service.PerService;

/**
 * @author liguangcai
 * @date 2018/08/31 11:05
 */
@Service
@Transactional
public class PerServiceImpl implements PerService {

	@Autowired
	private JdbcTemplate template;
	@Autowired
	private DataSourceProperties dbProp;
	@Autowired
	private PerProperties perProp;
	
	private String tableName() {
		return perProp.getTableName();
	}

	private final static String INSERT, SELECT_VAL, SELECT_ALL, SELECT, DELETE, FUZZTY_SELECT, FUZZY_DELETE,EXPIRED_ALL,
	EXTEND_KEY,PER_TAG;
	static {
		INSERT = "insert into %s (`key`, `value`,`expire_time`) values ('%s', '%s' ,'%s')";
		SELECT = "select * from %s as p where p.expire_time >= '%s' ";
		SELECT_VAL = "select p.value from %s as p where p.key = '%s' and expire_time >= '%s'";
		SELECT_ALL = "select * from %s as p where p.key = '%s' and expire_time >= '%s' ";
		FUZZTY_SELECT = "select * from %s as p where p.key like '%s' and expire_time >= '%s' ";
		DELETE = "delete from %s where 	`key` = '%s' ";
		FUZZY_DELETE = "delete from %s where `key` like '%s' ";
		EXPIRED_ALL = "update %s set expire_time = '%s' where expire_time > '%s' ";
		EXTEND_KEY = "update %s set expire_time = '%s' where `key` = '%s' ";
		PER_TAG = "%";
	}
//	INSERT INTO `cat`.`t_persistent` (`key`, `value`, `exprie_time`) VALUES ('1', '1-value', '2019-07-16 10:37:07');
//	select * from t_persistent where expire_time >= '2019-07-16 10:30:07' 
	@Override
	public void save(PerModel perModel) {
		String time = DateUtil.toLocalDateTime(Instant.now());
		template.execute(String.format(INSERT, tableName(), perModel.getKey(), perModel.getValue(),time));
	}

	@Override
	public String getByKey(String key) {
		String time = DateUtil.toLocalDateTime(Instant.now());
		List<Map<String, Object>> listMap = template.queryForList(String.format(SELECT_VAL, tableName(), key,time));
		if (listMap.size() > 0) {
			return listMap.get(0).get("value").toString();
		}
		return null;
	}

	@Override
	public PerModel getModelByKey(String key) {
		String time = DateUtil.toLocalDateTime(Instant.now());
		List<Map<String, Object>> listMap = template.queryForList(String.format(SELECT_ALL, tableName(), key,time));
		if (listMap.size() > 0) {
			Map<String,	Object> map = listMap.get(0);
			String expireTime = map.get("expire_time").toString();
			return new PerModel(String.valueOf(map.get("key")),JSON.parse(map.get("value").toString()).toString(),
					DateUtil.toInstant(expireTime.split("\\.")[0]));
		}
		return null;
	}

	@Override
	public List<String> getByPrefix(String prefix) {
		String time = DateUtil.toLocalDateTime(Instant.now());
		List<Map<String, Object>> listMap = template
				.queryForList(String.format(FUZZTY_SELECT, this.tableName(), prefix + PER_TAG,time));
		if (listMap.size() > 0) {
			return listMap.stream().map(map -> String.valueOf(map.get("key"))).collect(Collectors.toList());
		}
		return null;
	}

	@Override
	public List<String> getByFuzzy(String fuzzy) {
		return getByPrefix(PER_TAG + fuzzy);
	}

	@Override
	public void delByKey(String key) {
		template.execute(String.format(DELETE, tableName(), key));
	}

	@Override
	public void delByPrefix(String prefix) {
		template.execute(String.format(FUZZY_DELETE, tableName(), prefix + PER_TAG));
	}

	@Override
	public void delByFuzzy(String fuzzy) {
		delByPrefix(PER_TAG + fuzzy);
	}

	@Override
	public boolean exits(String key) {
		if (null != getByKey(key)) {
			return true;
		}
		return false;
	}

	@Override
	public Map<String, String> getAll() {
		String time = DateUtil.toLocalDateTime(Instant.now());
		List<Map<String, Object>> listMap = template.queryForList(String.format(SELECT, tableName(),time));
		return toMap(listMap);
	}

	@Override
	public Map<String, String> getAllByPrefix(String prefix) {
		String time = DateUtil.toLocalDateTime(Instant.now());
		List<Map<String, Object>> listMap = template
				.queryForList(String.format(FUZZTY_SELECT, tableName(), prefix + PER_TAG,time));
		return toMap(listMap);
	}

	@Override
	public Map<String, String> getAllByFuzzy(String fuzzy) {
		String time = DateUtil.toLocalDateTime(Instant.now());
		List<Map<String, Object>> listMap = template
				.queryForList(String.format(FUZZTY_SELECT, tableName(), PER_TAG + fuzzy + PER_TAG,time));
		return toMap(listMap);
	}

	@Override
	public String getDBName() {
		String one = StringUtils.substringBeforeLast(dbProp.getUrl(), "?");
		String[] strings = one.split("/");
		return strings[strings.length - 1];
	}
	
	@Override
	public int expiredAll() {
		String time = DateUtil.toLocalDateTime(Instant.now());
		return template.update(String.format(EXPIRED_ALL,this.tableName(), time,time));
	}

	private Map<String, String> toMap(List<Map<String, Object>> listMap) {
		if (listMap.size() > 0) {
			Map<String, String> resultMap = new HashMap<>();
			listMap.stream().forEach(map -> {
				resultMap.put(String.valueOf(map.get("key")), String.valueOf(map.get("value")));
			});
			return resultMap;
		}
		return null;
	}

	@Override
	public int extendTime(String key, Long time) {
		PerModel perModel = this.getModelByKey(key);
		if (null!=perModel) {
			String timeStr = DateUtil.toLocalDateTime(perModel.getExpireTime().plusSeconds(time));
			return template.update(String.format(EXTEND_KEY, this.tableName(),timeStr,key));
		}
		return 0;
	}

}
